﻿

using Microsoft.Extensions.Caching.Distributed;
using Newtonsoft.Json;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace NetCore.Cache
{
    public class RedisService : ICacheService,IDisposable
    {
        protected IDatabase _cache;

        private ConnectionMultiplexer _connection;

        private readonly string _dbinstance;

        private static RedisService _redisHelper;

    

        public TimeSpan ExpireTime
        {
            get {
                return TimeSpan.FromMinutes(ApplicationEnvironments.Site.SessionTimeout);
            }
        }
        public static RedisService Instance
        {
            get {
                if (_redisHelper==null)
                {
                    _redisHelper = new RedisService();
                }
                return _redisHelper;
            }
        }
        private int _database ;
        public RedisService()
        {
            _database = ApplicationEnvironments.Site.Redis.DataBase;
            //ConfigurationOptions config = new ConfigurationOptions
            //{
            //    EndPoints =
            //    {
            //        { "redis0", 6380 },
            //        { "redis1", 6380 },
            //        { "redis2",6380 }
            //    },
            //     CommandMap = CommandMap.Create(ApplicationEnvironments.Site.Redis.CommandMap, available: false),
            //    KeepAlive = ApplicationEnvironments.Site.Redis.KeepAlive,
            //    DefaultVersion = new Version(2, 8, 8),
            //    Password = ApplicationEnvironments.Site.Redis.Password
            //};
            _connection = ConnectionMultiplexer.Connect(ApplicationEnvironments.Site.Redis.Connection);
            _cache = _connection.GetDatabase(_database);
            _dbinstance = ApplicationEnvironments.Site.Redis.InstanceName;
        }

        public static IConnectionMultiplexer GetMultiplexer()
        {
            return ConnectionMultiplexer.Connect(ApplicationEnvironments.Site.Redis.Connection);
        }
     
        public string GetKeyForRedis(string key)
        {
            return _dbinstance + key;
        }
        #region add
        /// <summary>
        /// 添加缓存
        /// </summary>
        /// <param name="key">缓存Key</param>
        /// <param name="value">缓存Value</param>
        /// <returns></returns>
        public bool Add(string key, object value)
        {
            return this.Add(key, value, ExpireTime);
        }
        /// <summary>
        /// 添加缓存
        /// </summary>
        /// <param name="key">缓存Key</param>
        /// <param name="value">缓存Value</param>
        /// <param name="expiresSliding">滑动过期时长（如果在过期时间内有操作，则以当前时间点延长过期时间,Redis中无效）</param>
        /// <param name="expiressAbsoulte">绝对过期时长</param>
        /// <returns></returns>
        public bool Add(string key, object value, TimeSpan expiresSliding, TimeSpan expiressAbsoulte)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }
           
            return _cache.StringSet(GetKeyForRedis(key), Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(value)), expiressAbsoulte);
        }
        /// <summary>
        /// 添加缓存
        /// </summary>
        /// <param name="key">缓存Key</param>
        /// <param name="value">缓存Value</param>
        /// <param name="expiresIn">缓存时长</param>
        /// <param name="isSliding">是否滑动过期（如果在过期时间内有操作，则以当前时间点延长过期时间,Redis中无效）</param>
        /// <returns></returns>
        public bool Add(string key, object value, TimeSpan expiresIn, bool isSliding = false)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }
          
            return _cache.StringSet(GetKeyForRedis(key), Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(value)), expiresIn);
        }

        public Task<bool> AddAsync(string key, object value)
        {
            return Task.Factory.StartNew<bool>(() => { return this.Add(key, value); });
        }

        public Task<bool> AddAsync(string key, object value, TimeSpan expiresSliding, TimeSpan expiressAbsoulte)
        {
            return Task.Factory.StartNew<bool>(() => { return this.Add(key, value, expiresSliding, expiressAbsoulte); });
        }

        public Task<bool> AddAsync(string key, object value, TimeSpan expiresIn, bool isSliding = false)
        {
            return Task.Factory.StartNew<bool>(() => { return this.Add(key, value, expiresIn, isSliding); });
        }
        #endregion

        #region 验证是否存在
        /// <summary>
        /// 验证缓存项是否存在
        /// </summary>
        /// <param name="key">缓存Key</param>
        /// <returns></returns>
        public bool Exists(string key)
        {
            if (string.IsNullOrWhiteSpace(key))
            {
                throw new ArgumentNullException(nameof(key));
            }
            return _cache.KeyExists(GetKeyForRedis(key));
        }

        public Task<bool> ExistsAsync(string key)
        {
            //if (key == null)
            //{
            //    throw new ArgumentNullException(nameof(key));
            //}
            //object cached;
            // bool flag=_cache.TryGetValue(key, out cached);
            return Task.Factory.StartNew<bool>(() => { return this.Exists(key); });
        }
        #endregion

        #region Get
        /// <summary>
        /// 获取缓存
        /// </summary>
        /// <param name="key">缓存Key</param>
        /// <returns></returns>
        public T GetCache<T>(string key) where T : class, new()
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }

            var value = _cache.StringGet(GetKeyForRedis(key));

            if (!value.HasValue)
            {
                return default(T);
            }

            return JsonConvert.DeserializeObject<T>(value);
        }

        public T GetOrCreate<T>(string key, Func<T> acquire) where T : class, new()
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }
            if (this.Exists(key))
            {
                var value = _cache.StringGet(GetKeyForRedis(key));
                if (!value.HasValue)
                {
                    return default(T);
                }

                return JsonConvert.DeserializeObject<T>(value);
            }
            var result = acquire();
            this.Add(key, result);
            return result;

        }
        public T GetOrCreate<T>(string key, Func<T> acquire, bool isSliding ) where T : class, new()
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }
            if (this.Exists(key))
            {
                var value = _cache.StringGet(GetKeyForRedis(key));
                if (!value.HasValue)
                {
                    return default(T);
                }

                return JsonConvert.DeserializeObject<T>(value);
            }
            var result = acquire();
            if (result == null)
            {
                return result;
            }
            this.Add(key, result, TimeSpan.FromMinutes(ApplicationEnvironments.Site.SessionTimeout), isSliding);
            return result;

        }
        /// <summary>
        /// 获取缓存
        /// </summary>
        /// <param name="key">缓存Key</param>
        /// <returns></returns>
        public object GetCache(string key)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }

            var value = _cache.StringGet(GetKeyForRedis(key));

            if (!value.HasValue)
            {
                return null;
            }
            return JsonConvert.DeserializeObject(value);
        }
        /// <summary>
        /// 获取缓存集合
        /// </summary>
        /// <param name="keys">缓存Key集合</param>
        /// <returns></returns>
        public IDictionary<string, object> GetAll(List<string> keys)
        {
            if (keys == null)
            {
                throw new ArgumentNullException(nameof(keys));
            }
            var dict = new Dictionary<string, object>();

            keys.ToList().ForEach(item => dict.Add(item, GetCache(GetKeyForRedis(item))));

            return dict;
        }

         

        public Task<IDictionary<string, object>> GetAllAsync(List<string> keys)
        {
            return Task.Factory.StartNew<IDictionary<string, object>>(() => { return this.GetAll(keys); });
        }

        public Task<T> GetCacheAsync<T>(string key) where T : class, new()
        {
            return Task.Factory.StartNew<T>(() => { return this.GetCache<T>(key); });
        }
        public Task<T> GetCacheAsync<T>(string key, Func<T> acquire) where T : class, new()
        {
            return Task.Factory.StartNew<T>(() => { return this.GetOrCreate<T>(key, acquire); });
        }

        public Task<object> GetCacheAsync(string key)
        {
            return Task.Factory.StartNew(() => { return this.GetCache(key); });
        }
        #endregion

        #region Remove
        /// <summary>
        /// 删除缓存
        /// </summary>
        /// <param name="key">缓存Key</param>
        /// <returns></returns>
        public bool RemoveCache(string key)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }
            return _cache.KeyDelete(GetKeyForRedis(key));
        }
        /// <summary>
        /// 批量删除缓存
        /// </summary>
        /// <param name="key">缓存Key集合</param>
        /// <returns></returns>
        public void RemoveAll(List<string> keys)
        {
            if (keys == null)
            {
                throw new ArgumentNullException(nameof(keys));
            }

            keys.ForEach(item => RemoveCache(item));
        }
        public Task RemoveAllAsync(List<string> keys)
        {
            return Task.Factory.StartNew(() => { this.RemoveAll(keys); });
        }

        public Task<bool> RemoveCacheAsync(string key)
        {
            return Task.Factory.StartNew<bool>(() => { return this.RemoveCache(key); });
        }
        #endregion





        public void Dispose()
        {
            if (_cache != null)
                _connection.Dispose();
            GC.SuppressFinalize(this);
        }
      
        public bool Clear()
        {
            var keys = new List<string>();
            //遍历集群内服务器
            foreach (var endPoint in _connection.GetEndPoints())
            {
                //获取指定服务器
                var server = _connection.GetServer(endPoint);
                //在指定服务器上使用 keys 或者 scan 命令来遍历key
                server.FlushDatabase(_database);
                //foreach (var key in server.Keys(_database))
                //{
                //    //获取key对于的值
                   
                    
                //    _cache.KeyDelete(key);
                //}
            }
          // this.RemoveAll(keys);
            return true;
        }


        public byte[] Get(string key)
        {
            var obj = this.GetCache(key);
            if (obj == null||string.IsNullOrWhiteSpace(obj.ToString()))
            {
                return null;
            }
            return Convert.FromBase64String(obj.ToString());
        }

        public Task<byte[]> GetAsync(string key, CancellationToken token = default(CancellationToken))
        {
            return Task.Factory.StartNew<byte[]>(() => {

                return this.Get(key);
            });
        }

        public void Set(string key, byte[] value, DistributedCacheEntryOptions options)
        {
            this.Add(key, Convert.ToBase64String(value), options.SlidingExpiration.HasValue ? options.SlidingExpiration.Value : this.ExpireTime);
        }

        public Task SetAsync(string key, byte[] value, DistributedCacheEntryOptions options, CancellationToken token = default(CancellationToken))
        {
            return Task.Factory.StartNew(() => {

                this.Add(key, Convert.ToBase64String(value), options.SlidingExpiration.HasValue ? options.SlidingExpiration.Value : this.ExpireTime);
            });
        }

        public void Refresh(string key)
        {
            if (Exists(key))
            {
                this.Add(key, GetCache(key));
            }
        }

        public Task RefreshAsync(string key, CancellationToken token = default(CancellationToken))
        {
            return Task.Factory.StartNew(() => {
                this.Add(key, GetCache(key));
            });
        }

        public void Remove(string key)
        {
            this.RemoveCache(key);
        }

        public Task RemoveAsync(string key, CancellationToken token = default(CancellationToken))
        {
            return Task.Factory.StartNew(() => {
                this.RemoveCache(key);
            });
        }
    }
}
